home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-02-10 | 34.3 KB | 1,722 lines |
-
- LOS TRUCOS DEL LABORATORIO
-
-
- PROGRAMAS PROFESIONALES
-
- Los tΘcnicos del Laboratorio de PC Actual evaluamos continuamente gran
- cantidad de programas, entre los que se encuentran los realizados por
- nuestros lectores. Por este motivo observamos ciertos detalles que
- hacen que los programas de nuestros lectores no lleguen a tener ese
- toque de profesionalidad tan deseable en toda aplicaci≤n.
-
- Cualquiera que haya programado alguna vez en C o Pascal y haya
- necesitado hacer uso de funciones grßficas, se habrß visto obligado a
- incluir en el mismo directorio en que se encontraba el programa
- definitivo los conocidos ficheros BGI (Borland Graphics Interface).
- Esta no es, desde luego, una soluci≤n muy profesional, puesto que
- cualquier descuido con estos BGI darφa al traste con nuestro programa.
-
- Uno de los detalles mßs sencillos de conseguir es la introducci≤n de
- dichos drivers de pantalla (.BGI) y de fuentes de letra (.CHR), dentro
- de los ejecutables realizados con compiladores de Borland (Turbo
- Pascal, Borland Pascal, Turbo C, etc).
-
- El siguiente ejemplo muestra c≤mo introducir el driver para tarjetas
- VGA (EgaVga.bgi) dentro de un ejecutable en programas de Pascal,
- haciendo uso de la utilidad BINOBJ (que por defecto se encuentra en el
- directorio ½\tp\utils╗), utilidad que convierte ficheros binarios en
- c≤digo objeto. Para ello teclearemos:
-
- BINOBJ egavga.bgi egavga egavga
-
- Tras esto obtendremos un fichero ½egavga.obj╗. Para utilizarlo
- bastarß con introducirlo dentro del programa en Pascal, para lo que
- crearemos la siguiente unidad:
-
- Unit tarjetas;
-
- Interface
-
- Procedure EgaVga;
-
- Implementation
-
- Procedure EgaVga;
-
- External;
-
- {$L EgaVga.OBJ}
-
- End.
-
- TEXR = En nuestro programa, en la declaraci≤n de unidades empleadas
- (lφnea ½USES╗), tendremos que incluir entonces la unidad ½tarjetas╗
- asφ definida. Para inicializar el modo grßfico procederemos de la
- siguiente forma:
-
- Procedure IniciaGraficos; {Inicia modo grßfico}
-
- Var GD,GM :Integer;
-
- Begin
-
- If RegisterBGIDriver (@EgaVga) <0 Then Halt (1);
-
- GD := VGA;
-
- GM := VGAHi;
-
- InitGraph(GD,GM,'');
-
- If GraphResult <0 then Halt(1);
-
- End;
-
- Para incorporar los archivos de las fuentes (.chr) procederemos de
- manera similar. Como podemos ver, esto tiene como ventaja que una vez
- hayamos realizado la conversi≤n a obj y tengamos la unidad ½tarjetas╗,
- no serß necesario volverlas a escribir, y prodremos usarlo en todos
- nuestros programas.
-
- Para llevar esto a cabo en los programas en Turbo C actuaremos de modo
- similar, sabiendo que Borland incluye con sus compiladores de C la
- utilidad ½bgiobj╗, que nos permite transformar ficheros BGI y CHR (los
- que contienen los tipos de letras) en ficheros OBJ, directamente
- enlazables con nuestro programa. Para este paso tenemos que dar la
- orden BGIOBJ ½fichero╗ (donde ½fichero╗ es cualquier archivo con
- extensi≤n BGI o CHR).
-
- A continuaci≤n podemos incluir los ficheros objeto asφ generados en
- alguna de las librerφas (por ejemplo, en graphics.lib). Para ello
- daremos la orden
-
- TLIB graphics + ½fichero objeto╗ + ½fichero objeto╗ + ...
-
- A la hora de montar el programa tenemos que indicarle al compilador
- que vamos a emplear estos ficheros, para lo cual es necesario utilizar
- las funciones ½registerbgidriver()╗ y ½registerbgifont()╗ en el
- programa principal antes de hacer la llamada a ½initgraph()╗. Estas
- funciones usan como argumento un nombre simb≤lico definido en
- graphics.h (EGAVGA_driver, ATT_driver, triplex_font, gothic_font,
- etc).
-
- Nota: Si no hemos a±adido los ficheros objeto a la librerφa grßfica,
- es necesario compilarlos con el programa fuente (o a±adirlos en el
- fichero de proyecto). Por ejemplo,
-
- TCC ½programa_fuente╗ GRAPHICS.LIB EGAVGA.OBJ TRIP.OBJ
-
-
-
- PROGRAMACION DE LA VGA EN MODO 320x200x256 (I)
-
- Compiladores como Turbo Pascal o Turbo C (o sus hermanos mayores
- Borland Pascal y Borland C/C++) ofrecen unos drivers de los que ya
- hemos hablado en numerosas ocasiones: los BGI (Borland Graphics
- Interface).
-
- Con estos drivers en modo VGA podemos conseguir la resoluci≤n de 640 x
- 480 con 16 colores, con la ventaja de tener un gran n·mero de
- funciones de dibujo y de salida de texto ya definidas. Este modo
- ofrece excelentes resultados para programas que requieran gran
- detalle, pero el problema surge cuando para nuestro objetivo
- necesitamos mayor n·mero de colores, pues 16 suelen ser pocos. En
- este caso podemos recurrir al modo 320 x 200 con 256 colores.
- Perdemos resoluci≤n, pero lo podemos subsanar mediante un buen uso de
- la paleta de colores. En muchas ocasiones el cambio es preferible,
- por lo que este modo es muy recomendable para ser utilizado en juegos.
-
- La desventaja que posee este modo frente al VGA estßndar ofrecido por
- el driver BGI es que no disponemos de funciones de dibujo ni de salida
- de texto, lo cual en principio puede asustar. Pero os aseguramos que
- es mßs fßcil de lo que parece, y desde estas lφneas vamos a intentar
- solventar todos los problemas que pueden aparecer.
-
- TambiΘn os recordamos que, aunque no se ofrezcan con los paquetes de
- Borland, circulan por ahφ gran cantidad de drivers BGI que sφ soportan
- este modo y los modos de SVGA (alcanzando incluso los 1.024 x 768 con
- 256 colores). Es fßcil encontrarlos en cualquiera de las BBS
- espa±olas (incluida por supuesto PC Actual BBS) con nombres tales como
- SVGABGI, VGA256 u otros similares. Pero contemos o no contemos con
- ellos podemos intentar acceder a la VGA a un nivel un poco mßs
- profundo, para conseguir mßs potencia y velocidad.
-
- En caso de disponer de los BGI para acceder al modo 320 x 200 x 256,
- tambiΘn conocido como modo 13h, simplemente inicializaremos como de
- costumbre con el comando ½initgraph╗ (tanto en C como en Pascal). Si
- no disponemos de estos drivers o no necesitamos las funciones que nos
- ofrecen, podemos inicializar el modo utilizando el assembler, cargando
- el valor 13h en el registro ax, y llamando despuΘs a la interrupci≤n
- 10. Por ejemplo, si estamos empleando Turbo Pascal podemos introducir
- el siguiente conjunto de instrucciones en ensamblador:
-
- asm
-
- mov ax,13h
-
- int 10h
-
- end;
-
- Si empleamos Turbo C o cualquiera de sus ½hermanos╗, la secci≤n de
- c≤digo anterior queda como sigue:
-
- asm {
-
- mov ax,13h
-
- int 10h
-
- }
-
- De esta forma, inicializaremos el modo 320 x 200 x 256. Una de las
- ventajas que ofrece este modo es que, para acceder a cualquier
- posici≤n de memoria, s≤lo necesita un segmento de 64.000 bytes (320 x
- 200 x 1 byte de color por pixel). De esta forma, para iluminar el
- pixel de coordenadas x e y s≤lo tendremos que cargar un byte en la
- direcci≤n de memoria A000h:y*320+x. Dicho byte corresponderß al valor
- del color con que lo queramos iluminar, entre 0 y 255.
-
- Por ejemplo, un simple programa en Pascal que dibuje 256 lφneas
- verticales, cada una de un color diferente, quedarφa como sigue:
-
- Program VGA;
-
- Uses Crt;
-
- Var x,y:integer;
-
- Begin
-
- asm
-
- mov ax,13h
-
- int 10h
-
- end;
-
- For x:=0 to 255 do
-
- For y:= 0 to 199 do
-
- Mem[$A000:y*320+x]:=x;
-
- Repeat Until KeyPressed;
-
- End.
-
- La misma rutina en C nos quedarφa como sigue:
-
- # include <dos.h>
-
- main ()
-
- {
-
- int x, y;
-
- asm MOV AX, 0x13;
-
- asm INT 0x10;
-
- for (x = 0; x <= 255; x++)
-
- for (y = 0; y <= 199; y++)
-
- poke (0xA000, y*320 + x, x);
-
- getch();
-
- }
-
- Tras ejecutar cualquiera de los anteriores programas, cuando volvamos
- al DOS nos encontraremos con que tenemos el modo de vφdeo cambiado y
- para volver al modo normal tendremos que ejecutar ½mode co80╗. Para
- evitar esto, podemos volver al modo de texto normal dentro del propio
- programa. Para ello tenemos dos posibilidades: bien con la
- instrucci≤n ½textmode (lastmode)╗ (com·n en C y Pascal, en C pertenece
- a la librerφa conio.h y en Pascal a la unidad Crt) o bien usando de
- nuevo el ensamblador, cargando un 3 en ax y ejecutando la interrupci≤n
- 10h.
-
- Por este mes terminamos con la programaci≤n de la VGA en el modo 13,
- pero seguiremos en sucesivos n·meros con este interesante tema. Y no
- olvidΘis que si vosotros tenΘis alguna rutina interesante tambiΘn
- podΘis mandarla y de paso conseguir alg·n que otro premio.
-
-
-
- LA PROGRAMACION DE LA VGA 320x200x256 (II)
-
- En el truco anterior iniciamos esta serie sobre la programaci≤n en uno
- de los modos de la VGA mßs potentes y sencillos al mismo tiempo. La
- posibilidad de usar 256 colores al mismo tiempo es algo que sobrepasa
- con creces la pΘrdida de resoluci≤n frente a los 640x480 con 16
- colores.
-
- Por ello, vamos a seguir profundizando un poco mßs en el modo grßfico
- mßs empleado, el conocido entre los programadores como modo 13. En el
- n·mero anterior explicamos la forma de inicializar la pantalla e
- iluminar un pixel de un color determinado. Dijimos que para iluminar
- el pixel de coordenadas x,y s≤lo hace falta cargar un byte en la
- direcci≤n de memoria A000h:y*320+x. Dicho byte corresponderß al valor
- (de 0 a 255) del color empleado para iluminarlo. La forma mßs
- sencilla de hacer esto en Pascal es mediante la instrucci≤n
- ½Mem[$A000:y*320+x]:=c;╗, donde ½c╗ es el color del que queremos
- ilumninar la posici≤n x,y.
-
- TambiΘn es posible hacerlo de forma inversa, es decir, escribir
- ½c:=Mem[$A000:y*320+x];╗. Con esta simple instrucci≤n obtendremos el
- color del pixel de coordenadas x,y. Con esto hemos conseguido simular
- las instrucciones ½PutPixel╗ y ½GetPixel╗ proporcionadas con los
- drivers BGI.
-
- Como hemos dicho, lo mejor de este modo grßfico es la cantidad de
- colores disponibles: tenemos en nuestra mano una paleta de 256
- colores a elegir entre un total de 261.844 colores. Esto es debido a
- que cada color lo podemos descomponer en diferentes intensidades de
- los colores primarios rojo, verde y azul. Estas intensidades pueden
- tomar valores del 0 al 63 para cada color primario, lo que nos da un
- total de 64 x 64 x 64 combinaciones diferentes, que es igual a 261.844
- colores. Por ejemplo, el rojo intenso tendra unos componentes de
- color de 63 para el tono rojo y de 0 para los componentes verde y
- azul.
-
- Manipulando estos valores podemos conseguir la perfecta representaci≤n
- de una imagen en pantalla, asφ como interesantes efectos entre los que
- cabe destacar los fundidos, tan comunes en los juegos.
-
- Pero ahora llega la gran pregunta, de quΘ forma se pueden leer y
- modifcar los valores de la paleta. Pues es muy sencillo. Para leer
- un valor de la paleta hay que introducir el n·mero del color que
- queremos leer en el puerto 3C7h, y despuΘs leer los valores de rojo,
- verde y azul respectivamente del puerto 3C9h. En Turbo Pascal los
- n·meros en hexadecimal se introducen con un ½$╗ delante, asφ el puerto
- 3C7h lo pondremos como $3c7, mientras que en ½C╗ y ½C++╗ los n·meros
- en hexadecimal se introducen con el prefijo ½0x╗: por ejemplo, el
- mismo n·mero lo escribiremos como 0x3C7.
-
- Para que quede mßs claro, en Turbo Pascal tendremos un ½procedure╗
- para leer el valor de un puerto, que quedarß como sigue:
-
- Procedure CogeColor(Color : Byte; Var R,V,A : Byte);
-
- { Lee las intensidades de Rojo, Verde y Azul de un Color }
-
- Begin
-
- Port[$3c7] := Color;
-
- R := Port[$3c9];
-
- V := Port[$3c9];
-
- A := Port[$3c9];
-
- End;
-
- Ahora vamos a explicar el proceso contrario, es decir, la forma de
- modificar las intensidades de rojo, verde y azul de un color
- cualquiera. Para ello hay que introducir en el puerto 3C8h el n·mero
- del color a modificar y despuΘs introducir los valores de rojo, verde
- y azul respectivamente en el puerto 3C9h. De nuevo la ½procedure╗
- quedarφa como sigue:
-
- Procedure PonColor(Color : Byte; R,G,B : Byte);
-
- { Esto asigna a un Color las intensidades de Rojo, Verde y Azul }
-
- Begin
-
- Port[$3c8] := Color;
-
- Port[$3c9] := R;
-
- Port[$3c9] := G;
-
- Port[$3c9] := B;
-
- End;
-
- En Turbo C empleamos el comando ½inportb( int port )╗ para leer un
- byte del puerto ½port╗, mientras que para escribir a un puerto usamos
- ½outportb( int port, unsigned char byte )╗. Ambos comandos pertenecen
- a la librerφa ½dos.h╗.
-
-
- PROGRAMACION DE LA VGA 320x200x256 (III)
-
- Ha llegado la hora de poner en prßctica algunos de los conocimientos
- que os hemos venido dando en las dos primeras ½lecciones╗ sobre la
- VGA. Ya hemos aprendido a inicializar el modo grßfico 320x200x256, a
- iluminar un punto o pixel con un determinado color y a modificar las
- intensidades de rojo, verde y azul de cada uno de los 256 colores.
-
- Vamos a realizar uno de los efectos mßs sencillos y a la vez mßs
- llamativos, el conocido como fade o fundido. Este efecto habitual en
- todo tipo de juegos consiste en pasar la imagen que tenemos en
- pantalla de una forma suave a tener la pantalla totalmente negra (o
- cualquier otro color). Y en el efecto contrario, pasar de una
- pantalla negra a la imagen con todo su color.
-
- Para efectuar los fundidos emplearemos los procedimientos ½CogeColor╗
- y ½PonColor╗ que ya explicamos anteriormente.
-
- Procedure PonColor (Color:Byte; R,G,B:Byte);
-
- Begin
-
- Port[$3c8]:=Color; Port[$3c9]:=R; Port[$3c9]:=G; Port[$3c9]:=B;
-
- End;
-
- Procedure CogeColor (Color:Byte; Var R,G,B:Byte);
-
- Begin
-
- Port[$3c7]:=Color; R:=Port[$3c9]; G:=Port[$3c9]; B:=Port[$3c9];
-
- End;
-
- Es posible dejar la pantalla en negro si se ponen los componentes RGB
- de todos los colores a 0. El procedimiento PonerNegro hace esto
- mismo:
-
- Procedure PonerNegro;
-
- Var i:Integer;
-
- Begin
-
- For i:=0 to 255 do
-
- PonColor (i,0,0,0);
-
- End;
-
- Para conseguir un buen efecto de fundido lo primero que debemos hacer
- es grabar toda la paleta, para ello emplearemos un array:
-
- Var Paleta := Array [0..255,1..3] of Byte;
-
- Type Pal: Record
-
- Rojo, verde, Azul : byte;
-
- End;
-
- Var Paleta : Array [0..255] Of Pal;
-
- Los valores de 0 a 255 son para los 256 colores de que consta el modo
- VGA que nos trata. Los valores 1 al 3 son para guardar las
- intensidades de rojo, verde y azul. Para grabar todos los colores de
- la paleta usaremos el siguiente procedimiento:
-
- Procedure GrabaPaleta;
-
- Var i: Integer;
-
- Begin
-
- For i := 0 to 255 do
-
- CogeColor (Loop, Paleta [i,1], Paleta [i,2], Paleta [i,3]);
-
- End;
-
- Con esto conseguimos grabar la paleta entera en la variable Paleta.
- Ahora podemos efectuar cualquier fundido sobre la pantalla. Para ello
- podemos emplear el siguiente procedimiento, que efect·a un fundido de
- la paleta actual a la que le indiquemos en la variable Paleta. Lo que
- hace el programa es ir comparando los valores actuales de Paleta con
- los de la paleta que queremos obtener; en caso de ser menores los
- incrementa.
-
- Procedure FadeToPal;
-
- Var i,j: Integer;
-
- Tmp : Array [1..3] of byte;
-
- { Esta variable almacena temporalmente los valores de cada color }
-
- Begin
-
- For i:=1 to 64 do Begin
-
- { Los valores de rojo, verde y azul para cada color toman valores
- del 0 al 63, por eso s≤lo necesitamos ejecutar el bucle un mßximo
- de 64 veces}
-
- For j:=0 to 255 do Begin
-
- CogeColor (j,Tmp[1],Tmp[2],Tmp[3]);
-
- If Tmp[1]<Paleta[j,1] then inc (Tmp[1]);
-
- If Tmp[2]<Paleta[j,2] then inc (Tmp[2]);
-
- If Tmp[3]<Paleta[j,3] then inc (Tmp[3]);
-
- { Si las componentes de rojo, verde o azul de cada color ½j╗ son
- menores del valor que debemos alcanzar las incrementamos en 1 }
-
- PonColor (j,Tmp[1],Tmp[2],Tmp[3]);
-
- { Fija los nuevos valores para los colores }
-
- End;
-
- End;
-
- End;
-
- Pero otro de los efectos clßsicos es el fundido de una imagen hasta
- alcanzar el negro. El efecto es el mismo pero al revΘs, asφ que lo
- que tendremos que invertir es el interior del bucle para que si los
- valores son mayores de 0, ir restando hasta alcanzar el 0.
-
- Procedure FadeToNegro;
-
- VAR i,j:integer;
-
- Tmp : Array [1..3] of byte;
-
- { Esta variable almacena temporalmente los valores de cada color }
-
- BEGIN
-
- For i:=1 to 64 do Begin
-
- For j:=0 to 255 do Begin
-
- CogeColor (j,Tmp[1],Tmp[2],Tmp[3]);
-
- If Tmp[1]>0 then dec (Tmp[1]);
-
- If Tmp[2]>0 then dec (Tmp[2]);
-
- If Tmp[3]>0 then dec (Tmp[3]);
-
- { Si las componentes de rojo, verde o azul de cada color ½j╗ son
- mayores que 0 les restamos 1, hasta que alcancen este valor }
-
- PonColor (j,Tmp[1],Tmp[2],Tmp[3]);
-
- { Fija los nuevos valores para los colores }
-
- End;
-
- End;
-
- End;
-
- Si vemos que los fundidos se realizan a gran velocidad podemos poner
- un delay para que sean mßs lentos y logren un mayor efecto. Realizar
- un fundido al negro de la pantalla es mucho mßs impresionante que
- limpiar simplemente la pantalla. Para restaurar la paleta original
- simplemente hay que poner de nuevo los valores de las componentes
- rojo, verde y azul de cada uno de los 256 colores. Lo podemos
- conseguir de la siguiente forma:
-
- Procedure RestauraPaleta;
-
- Var i:integer;
-
- Begin
-
- For i:=0 to 255 do
-
- PonColor (i, Paleta[i,1], Paleta [i,2], Paleta [i,3]);
-
- End;
-
- A lo largo de la explicaci≤n hemos considerado la variable Paleta como
- un array de 256x3 (256 colores x 3 componentes). Otra forma de
- tratar la paleta puede ser definiendo:
-
- Type Pal: Record
-
- Rojo, Verde, Azul : Byte;
-
- End;
-
- Var Paleta : Array [0..255] Of Pal;
-
- De esta forma en vez de referirnos a Paleta [i,1], Paleta [i,2] y
- Paleta [i,3] pondremos Paleta[i].Rojo, Paleta[i].Verde y Paleta
- [i].Azul, con lo cual evidentemente ganamos en claridad y comodidad.
-
-
-
- MEJORAR LA PRESENTACION
-
- Para mejorar la presentaci≤n de muchos programas, en especial los
- juegos, es conveniente adaptarlos a la velocidad del procesador,
- aunque antes deberemos saber quΘ procesador tiene instalado la mßquina
- sobre la que funcionarß el programa.
-
- Entre los programadores del Laboratorio TΘcnico hemos elgido la rutina
- que os adjuntamos por su facilidad de uso e implementaci≤n en
- diferentes lenguajes. Aquφ damos un ejemplo de c≤mo hacerlo en Turbo
- Pascal.
-
- El bloque principal es el siguiente programa en ensamblador, que
- grabaremos como ½chiptype.asm╗:
-
- CODE SEGMENT BYTE PUBLIC 'CODE'
-
- ASSUME CS:CODE
-
- PUBLIC chiptype
-
- chiptype PROC FAR
-
- control EQU WORD PTR [bp-2]
-
- push BP
-
- mov BP,SP
-
- push DI
-
- push SI
-
- push CX
-
- call cpu_type
-
- cmp DX,484
-
- jnz Not_486
-
- mov AX,DX
-
- jmp short Got_486
-
- Not_486:
-
- call ndp_type
-
- add AX,DX
-
-
- Got_486:
-
- pop CX
-
- pop SI
-
- pop DI
-
- pop BP
-
- ret
-
- chiptype endp
-
- cpu_type PROC NEAR
-
- pushf
-
- xor DX,DX
-
- xor AX,AX
-
- push AX
-
- popf
-
- pushf
-
- pop AX
-
- and AX, 0f000h
-
- cmp AX, 0f000h
-
- jz dig
-
- mov AX, 07000h
-
- push AX
-
- popf
-
- pushf
-
- pop AX
-
- and AX,07000h
-
- jnz got386
-
- mov DX, 0280
-
- jmp SHORT CPUbye
-
- got386:
-
- .386
-
- mov edx,esp
-
- and esp, not 3
-
- pushfd
-
- pop eax
-
- mov ecx,eax
-
- xor eax,40000h
-
- push eax
-
- popfd
-
- pushfd
-
- pop eax
-
- xor eax,ecx
-
- shr eax,18
-
- push ecx
-
- popfd
-
- mov esp,edx
-
- and eax,1
-
- .8086
-
- jz SHORT CPU386
-
- mov DX,484
-
- jmp SHORT CPUbye
-
- CPU386:
-
- mov DX,380
-
- jmp SHORT CPUbye
-
- dig: mov AX, 0ffffh
-
- mov CL, 33
-
- shl AX, CL
-
- jz digmor
-
- mov DX,0180
-
- jmp SHORT CPUbye
-
- digmor: xor AL,AL
-
- mov AL,40h
-
- mul AL
-
- jz gotNEC
-
- mov DX,0080
-
- jmp SHORT CPUbye
-
- gotNEC: mov DX,0200
-
- CPUbye: popf
-
- ret
-
- cpu_type endp
-
- ndp_type PROC NEAR
-
- do_we: fninit
-
- mov byte ptr control+1,0
-
- fnstcw control
-
- mov AH,byte ptr [control+1]
-
- cmp AH,03h
-
- je chk_87
-
- xor AX,AX
-
- jmp SHORT NDPbye
-
- chk_87: and control,NOT 0080h
-
- fldcw control
-
- fdisi
-
- fstcw control
-
- test control,0080h
-
- jz chk287
-
- mov AX,0001
-
- jmp SHORT NDPbye
-
- chk287: finit
-
- fld1
-
- fldz
-
- fdiv
-
- fld st
-
- fchs
-
- fcompp
-
- fstsw control
-
- fwait
-
- mov AX,control
-
- sahf
-
- jnz got387
-
- mov AX,0002
-
- jmp SHORT NDPbye
-
- got387: mov AX,0003
-
- NDPbye: ret
-
-
- ndp_type endp
-
- CODE ends
-
- end
-
- Una vez copiado lo ensamblamos con la orden ½Tasm chiptype.asm╗, lo
- que nos generarß el codigo objeto de este programa. Incluye la
- funci≤n ½Chiptype╗ que devuelve un valor entero dependiendo del
- procesador sobre el que corra el programa: mayor que 480 si es un
- 486, entre 380 y 480 si es un 386, si es un 286 nos da valores entre
- 280 y 380, mientra que si el valor devuelto es menor que 280 el
- procesador sera un 808x ≤ 8018x. Para comprobar si existe
- coprocesador instalado basta con hallar el valor de ½ChipType╗ en
- m≤dulo 10 y comprobar que es distinto de 0.
-
- Para implementar esta rutina en Turbo Pascal basta con escribir el
- siguiente programa, que nos crearß una ½unidad╗ que podremos emplear
- desde el programa principal.
-
- {$R-,S-,I-,D+,F-,V-,B-,N-,L+ }
-
- UNIT Chips;
-
- INTERFACE
-
- function ChipType : integer;
-
- IMPLEMENTATION
-
- {$L ChipType.obj }
-
- function ChipType; external;
-
- END.
-
- Este es el programa principal, el cual nos devuelve el
- procesador con el que estamos trabajando:
-
-
- PROGRAM cpucheck;
-
- USES
-
- Chips;
-
- var
-
- Chip_Value : integer;
-
- BEGIN
-
- Chip_Value := ChipType;
-
- if Chip_Value > 480 then
-
- Write ( 'El procesador es un 486' )
-
- else
-
- if Chip_Value > 380 then
-
- Write ( 'El procesador es un 386' )
-
- else
-
- if Chip_Value > 280 then
-
- Write ( 'El procesador es un 286' )
-
- else
-
- Write ( 'El procesador es un 808x o 8018x' );
-
- if Chip_Value > 480 then
-
- Writeln
-
- else
-
- if Chip_Value mod 10 <> 0 then
-
- Writeln ( ' Con Coprocesador' )
-
- else
-
- Writeln;
-
- END.
-
-
-
- EL ICONO DEL RATON
-
- Entre las consultas que mßs suelen llegar a nuestra redacci≤n se
- encuentran las referentes a la programaci≤n del rat≤n. Una de las
- dudas mßs comunes se refiere a la manera de modificar el icono del
- mismo. Mediante esta sencilla rutina en ensamblador se puede
- modificar la clßsica flecha por cualquier otro dibujo. El ·nico
- lφmite estß en que Θste deberß tener un tama±o de 16x16 pixels y s≤lo
- podrß tener un color.
-
- Con el fin de facilitar su implementaci≤n, hemos incluido el c≤digo
- ensamblador dentro del siguiente procedimiento, realizado en Turbo
- Pascal.
-
- procedure MiNuevoCursor; assembler;
-
- asm
-
- jmp @1
-
- dw 1111111111111111b { mßscara de pantalla : }
-
- dw 1111111111111111b { los 1's son pixels que }
-
- dw 1111100000001111b { "transparentan", los 0's }
-
- dw 1111100000001111b { son pixels donde el cursor }
-
- dw 1111000111000111b { es opaco. }
-
- dw 1100000111000001b
-
- dw 1100001111100001b
-
- dw 1100111111111001b
-
- dw 1100111111111001b
-
- dw 1100001111100001b
-
- dw 1100000111000001b
-
- dw 1111000111000111b
-
- dw 1111100000001111b
-
- dw 1111100000001111b
-
- dw 1111111111111111b
-
- dw 1111111111111111b
-
-
- dw 0000000000000000b { mßscara de cursor: }
-
- dw 0000000000000000b { los 1's son pixels que }
-
- dw 0000011111110000b { se encienden para dibujar }
-
- dw 0000011111110000b { el cursor. }
-
- dw 0000111000110000b
-
- dw 0011111000111110b
-
- dw 0011110000011110b
-
- dw 0011000000000110b
-
- dw 0011000000000110b
-
- dw 0011110000011110b
-
- dw 0011111000111110b
-
- dw 0000111000111000b
-
- dw 0000011111110000b
-
- dw 0000011111110000b
-
- dw 0000000000000000b
-
- dw 0000000000000000b
-
-
- @1:
-
- mov ax, cs
-
- mov es, ax
-
- mov dx, offset MiCursor+2
-
- mov ax, $0009
-
- mov bx,8 { coordenada x de control }
-
- mov cx,8 { coordenada y de control }
-
- int $33
-
- end;
-
- Como se puede observar, necesitamos dos mßscaras (todos los conjuntos
- de unos y ceros que representan el icono), ambas de 16x16. En la
- ½mßscara de pantalla╗ los unos (1) representan los pixels que quedan
- trasparentes mientras que los ceros (0) son los pixele donde el cursor
- queda opaco. La segunda mßscara es la ½mßscara del cursor╗, en la que
- todo ocurre al revΘs que en la anterior; es decir, basta con sustituir
- los unos por ceros y los ceros por unos, para conseguir esta segunda
- mßscara. En esta, los unos son los pixels que se encienden para
- dibujar el icono, mientras que los ceros representan las zonas
- traparentes.
-
- TambiΘn habrß que tener en cuenta las coordenadas del punto de
- control, es decir, el punto sobre el que se dan las coordenadas del
- rat≤n. Si tenemos una flecha, el punto de control serß la punta de la
- misma. En el ejemplo presentado, una especie de cφrculo, el punto de
- control se situa en el centro del cφrculo.
-
-
-
- IDENTIFICAR EL PROCESADOR
-
- Para mejorar la presentaci≤n de muchos programas, en especial los
- juegos, es conveniente adaptar el programa a la velocidad del
- procesador, pero para ello debemos saber quΘ chip tiene instalado la
- mßquina sobre la que funcionarß el programa. Hace dos meses, en la
- secci≤n de ½Microconsultas╗ ya indicamos una rutina en ensamblador que
- servφa para estos menesteres.
-
- Pero como dicha rutina podφa resultar difφcil de implementar en
- nuestras aplicaciones, entre los programadores del Laboratorio TΘcnico
- hemos elegido la siguiente rutina. Sus caracterφsticas fundamentales
- son su facilidad de uso e implementaci≤n en diferentes lenguajes.
- Aquφ vamos a daros un ejemplo de c≤mo hacerlo en Turbo Pascal.
-
- El bloque principal es el siguiente programa en ensamblador, que
- grabaremos como ½chiptype.asm╗:
-
- CODE SEGMENT BYTE PUBLIC 'CODE'
-
- ASSUME CS:CODE
-
- PUBLIC chiptype
-
- chiptype PROC FAR
-
- control EQU WORD PTR [bp-2]
-
- push BP
-
- mov BP,SP
-
- push DI
-
- push SI
-
- push CX
-
- call cpu_type
-
- cmp DX,484
-
- jnz Not_486
-
- mov AX,DX
-
- jmp short Got_486
-
- Not_486:
-
- call ndp_type
-
- add AX,DX
-
-
- Got_486:
-
- pop CX
-
- pop SI
-
- pop DI
-
- pop BP
-
- ret
-
- chiptype endp
-
- cpu_type PROC NEAR
-
- pushf
-
- xor DX,DX
-
- xor AX,AX
-
- push AX
-
- popf
-
- pushf
-
- pop AX
-
- and AX, 0f000h
-
- cmp AX, 0f000h
-
- jz dig
-
- mov AX, 07000h
-
- push AX
-
- popf
-
- pushf
-
- pop AX
-
- and AX,07000h
-
- jnz got386
-
- mov DX, 0280
-
- jmp SHORT CPUbye
-
- got386:
-
- .386
-
- mov edx,esp
-
- and esp, not 3
-
- pushfd
-
- pop eax
-
- mov ecx,eax
-
- xor eax,40000h
-
- push eax
-
- popfd
-
- pushfd
-
- pop eax
-
- xor eax,ecx
-
- shr eax,18
-
- push ecx
-
- popfd
-
- mov esp,edx
-
- and eax,1
-
- .8086
-
- jz SHORT CPU386
-
- mov DX,484
-
- jmp SHORT CPUbye
-
- CPU386:
-
- mov DX,380
-
- jmp SHORT CPUbye
-
- dig: mov AX, 0ffffh
-
- mov CL, 33
-
- shl AX, CL
-
- jz digmor
-
- mov DX,0180
-
- jmp SHORT CPUbye
-
- digmor: xor AL,AL
-
- mov AL,40h
-
- mul AL
-
- jz gotNEC
-
- mov DX,0080
-
- jmp SHORT CPUbye
-
- gotNEC: mov DX,0200
-
- CPUbye: popf
-
- ret
-
- cpu_type endp
-
- ndp_type PROC NEAR
-
- do_we: fninit
-
- mov byte ptr control+1,0
-
- fnstcw control
-
- mov AH,byte ptr [control+1]
-
- cmp AH,03h
-
- je chk_87
-
- xor AX,AX
-
- jmp SHORT NDPbye
-
- chk_87: and control,NOT 0080h
-
- fldcw control
-
- fdisi
-
- fstcw control
-
- test control,0080h
-
- jz chk287
-
- mov AX,0001
-
- jmp SHORT NDPbye
-
- chk287: finit
-
- fld1
-
- fldz
-
- fdiv
-
- fld st
-
- fchs
-
- fcompp
-
- fstsw control
-
- fwait
-
- mov AX,control
-
- sahf
-
- jnz got387
-
- mov AX,0002
-
- jmp SHORT NDPbye
-
- got387: mov AX,0003
-
- NDPbye: ret
-
-
- ndp_type endp
-
- CODE ends
-
- end
-
- Una vez copiado, lo ensamblaremos con la orden ½tasm chiptype.asm╗, lo
- que nos generarß el codigo objeto. Este programa incluye la funci≤n
- ½Chiptype╗, que devuelve un valor entero dependiendo del procesador
- sobre el que corra: si es un 486 devuelve un valor mayor que 480; si
- es un 386 devuelve un valor entre 380 y 480; si es un 286 nos da
- valores entre 280 y 380; mientras que si el valor devuelto es menor
- que 280, el procesador sera un 808x o 8018x. Para comprobar si existe
- coprocesador instalado basta con hallar el valor de ½ChipType╗ en
- m≤dulo 10 y comprobar que es distinto de 0.
-
- Para implementar esta rutina en Turbo Pascal basta con escribir la
- siguiente rutina, que nos crearß una unidad para ser usada desde el
- programa principal y de esta forma simplificar el proceso.
-
- {$R-,S-,I-,D+,F-,V-,B-,N-,L+ }
-
- UNIT Chips;
-
- INTERFACE
-
- function ChipType : integer;
-
- IMPLEMENTATION
-
- {$L ChipType.obj }
-
- function ChipType; external;
-
- END.
-
- Este es el programa principal, el cual nos devuelve el
- procesador sobre el que estß funcionando nuestra particular
- aplicaci≤n:
-
- PROGRAM cpucheck;
-
- USES
-
- Chips;
-
- var
-
- Chip_Value : integer;
-
- BEGIN
-
- Chip_Value := ChipType;
-
- if Chip_Value > 480 then
-
- Write ( 'El procesador es un 80486' )
-
- else
-
- if Chip_Value > 380 then
-
- Write ( 'El procesador es un 80386' )
-
- else
-
- if Chip_Value > 280 then
-
- Write ( 'El procesador es un 80286' )
-
- else
-
- Write ( 'El procesador es un 808x o 8018x' );
-
- if Chip_Value > 480 then
-
- Writeln
-
- else
-
- if Chip_Value mod 10 <> 0 then
-
- Writeln ( ' Con Coprocesador' )
-
- else
-
- Writeln;
-
- END.
-
-
- PARAMETROS DE DISCOS DUROS
-
- Muchos usuarios tras un borrado accidental de los datos de la BIOS, o
- cualquier circunstancia similar, pierden todo el acceso a su disco
- duro. En estos casos s≤lo es posible recuperar los datos de nuestro
- disco si conseguimos reconfigurar correctamente los parßmetros que
- deben ir en la BIOS. El problema surge cuando no tenemos apuntados
- dichos valores, precauci≤n que deberφamos tomar siempre que
- comprßsemos un disco duro. Y probar una a una todas las combinaciones
- posibles de cilindros, cabezas y sectores puede resultar una labor
- imposible. Ademßs, ya se sabe que los problemas s≤lo surgen cuando no
- pueden ser solucionados.
-
- El siguiente programita, escrito en C, nos darß todos los datos de
- nuestro disco duro, proporcionando tanto cilindros como cabezas y
- sectores, dando tanto los que lee de la BIOS como los que lee del
- propio disco duro.
-
- #define _PCACTUAL_
-
- #include <stdlib.h>
-
- #include <dos.h>
-
- #include <stdio.h>
-
- #include <conio.h>
-
- #include <bios.h>
-
- char *getascii (unsigned int in_data [], int off_start, int off_end);
-
- void main (void)
-
- {
-
- unsigned int dd [256];
-
- unsigned int dd_off;
-
- unsigned int loop;
-
- int num_drv; /* Numero de discos duros */
-
- union REGS registers;
-
- unsigned int bios_cyl [2], bios_head [2], bios_sec [2];
-
- /* Cilindros, Cabezas, Sectores */
-
- clrscr ();
-
- /* Cuantos discos y parßmetros */
-
- num_drv = peekb (0x40, 0x75);
-
- /* area de datos de la BIOS, Numero de discos duros */
-
- /* Byte en el segmento 40H Offset 75H */
-
- for (loop = 0; loop < num_drv; loop++)
-
- /* Buscar a travΘs de los discos */
-
- {
-
- /* Coger informaci≤n del disco IDE */
-
- while (inp (0x1F7) != 0x50);
-
- /* Espera a que la controladora no estΘ ocupada */
-
- outp (0x1F6, (loop == 0 ? 0xA0 : 0xB0));
-
- /* Coger primer/segundo disco */
-
- outp (0x1F7, 0xEC);
-
- /* Coger informaci≤n del disco */
-
- while (inp (0x1F7) != 0x58);
-
- /* Esperar a que estΘ listo para datos */
-
- for (dd_off = 0; dd_off != 256; dd_off++) /* Leer "sector" */
-
- dd [dd_off] = inpw (0x1F0);
-
- /* Coger informaci≤n del disco de la BIOS */
-
- registers.h.ah = 0x8;
-
- /* Coger informaci≤n del disco */
-
- registers.h.dl = 0x80 + loop;
-
- /* Disco es 80H para el disco 0, 81H para el 1 */
-
- int86 (0x13, ®isters, ®isters);
-
- if (! registers.x.cflag)
-
- {
-
- bios_head [loop] = registers.h.dh + 1;
-
- /* Las cabezas desde el 0 */
-
- bios_sec [loop] = registers.h.cl & 0x3F;
-
- /* sectores es bits 5 - 0 */
-
- bios_cyl [loop] = ((registers.h.cl & 0xC0) << 2) +
- registers.h.ch + 2;
-
- }
-
- clrscr ();
-
- fprintf (stdout, "DISCO %d:\n", loop);
-
- fprintf (stdout, "N·mero de modelo__________: %s\n", getascii (dd, 27, 46));
-
- fprintf (stdout, "N·mero de serie___________: %s\n", getascii (dd, 10, 19));
-
- fprintf (stdout, "Resultados del disco duro\n");
-
- fprintf (stdout, "Numero de cilindros (Fijado)______: %6u\n", dd [1]);
-
- fprintf (stdout, "Numero de cabezas_________________: %6u\n", dd [3]);
-
- fprintf (stdout, "Numero de sectores por pista______: %6u\n\n", dd [6]);
-
- fprintf (stdout, "Resultados de la BIOS\n");
-
- fprintf (stdout, "Numero de cilindros____________: %6u\n", bios_cyl [loop]);
-
- fprintf (stdout, "Numero de cabezas_____________: %6u\n", bios_head [loop]);
-
- fprintf (stdout, "Numero de sectores por pista__: %6u\n\n", bios_sec [loop]);
-
- if (! loop)
-
- {
-
- fprintf (stdout, "Pulsa una tecla");
-
- getch ();
-
- }
-
- }
-
- }
-
- char *getascii (unsigned int in_data [], int off_start, int off_end)
-
- {
-
- static char ret_val [255];
-
- int loop, loop1;
-
- for (loop = off_start, loop1 = 0; loop <= off_end; loop++)
-
- {
-
- ret_val [loop1++] = (char) (in_data [loop] / 256); /* Coge el byte alto */
-
- ret_val [loop1++] = (char) (in_data [loop] % 256); /* Coge el byte bajo */
-
- }
-
- ret_val [loop1] = '\0';
-
- /* Nos aseguramos de que termina en carßcter nulo */
-
- return (ret_val);
-
- }
-
-
- ½SHELLS╗ LIMPIOS
-
- Cuando escribimos un programa que efect·a llamadas externas a otras
- aplicaciones necesitamos usar la funci≤n ½Exec(Nombre_de_programa,
- Linea_de_comandos╗. Esto funciona perfectamente, pero nos podemos
- encontrar con un grave incoveniente: la salida se redirecciona a la
- pantalla, escribiendo mensajes que en ocasiones no deseamos que se
- vean. Un ejemplo lo encontramos cuando descomprimimos un programa,
- llamamos al descompresor con ½Exec╗ y en pantalla se nos muestra toda
- la informaci≤n del proceso de descompresi≤n.
-
- En estas ocasiones quedarφa mßs elegante presentar en pantalla un
- mensaje como ½Trabajando...╗, ½Instalando...╗ o algo similar.
- Podremos conseguir este aspecto, sin duda mucho mßs profesional,
- mediante el uso de la siguiente unidad, escrita en Turbo Pascal (como
- se hace uso del ensamblador incorporado es necesario la versi≤n 6.0 ≤
- superior). A pesar de estar escrita en Turbo Pascal, los usuarios del
- lenguaje C, no encontrarßn excesivas dificultades para adaptarla a sus
- necesidades.
-
- unit Mejora_Exec;
-
- interface
-
- Uses Dos;
-
- function SetOutput(Nombre_Fichero: PathStr): Boolean;
-
- procedure CancelOutput;
-
- implementation
-
- const OutRedir: Boolean = False;
-
- function SetOutput(Nombre_Fichero: PathStr): Boolean;
-
- begin
-
- Nombre_Fichero:=Nombre_Fichero+#0;
-
- SetOutput:=False;
-
- asm
-
- push ds
-
- mov ax, ss
-
- mov ds, ax
-
- lea dx, Nombre_Fichero[1]
-
- mov ah, 3Ch
-
- int 21h
-
- pop ds
-
- jnc @@1
-
- ret
-
- @@1:
-
- push ax
-
- mov bx, ax
-
- mov cx, Output.FileRec.Handle
-
- mov ah, 46h
-
- int 21h
-
- mov ah, 3Eh
-
- pop bx
-
- jnc @@2
-
- ret
-
- @@2:
-
- int 21h
-
- end;
-
- OutRedir:=True;
-
- SetOutput:=True;
-
- end;
-
- procedure CancelOutput;
-
- var
-
- Nombre_Fichero: String[4];
-
- begin
-
- if not OutRedir then Exit;
-
- Nombre_Fichero:='CON'#0;
-
- asm
-
- push ds
-
- mov ax, ss
-
- mov ds, ax
-
- lea dx, Nombre_Fichero[1]
-
- mov ax, 3D01h
-
- int 21h
-
- pop ds
-
- jnc @@1
-
- ret
-
- @@1:
-
- push ax
-
- mov bx, ax
-
- mov cx, Output.FileRec.Handle
-
- mov ah, 46h
-
- int 21h
-
- mov ah, 3Eh
-
- pop bx
-
- int 21h
-
- end;
-
- OutRedir:=False;
-
- end;
-
- end.
-
- Esta unidad nos permitirß redireccionar la salida estßndar a cualquier
- fichero (en vez de al monitor), dado por la variable ½Nombre_Fichero╗.
- Esta variable puede incluso tomar el valor NUL, en cuyo caso no se
- producirß ninguna salida, ni a fichero ni a pantalla.
-
- En el programa principal haremos:
-
- SetOutput('NUL');
-
- Exec(....);
-
- CancelOutput;
-
-
-